程式碼解釋整理
if (!isCurrentAnswerCorrect): 如果答案不正確,則延遲 500 毫秒後觸發重新排序。handleAnimationEnd(): 在動畫結束後,關閉動畫並排序資料。isAnimation: 用於判斷是否正在進行過渡效果,避免干擾。@transitionend="handleAnimationEnd": 在過渡效果結束後,執行 handleAnimationEnd()。如果資料不正確觸發重新排序
if (!isCurrentAnswerCorrect) {
    setTimeout(() => {
        handleUpdateTimelineTargetPosition(gameStatus.currentStep);
        isAnimation.value = true;
    }, 500);
}
為什麼要使用 setTimeout?我也嘗試使用 Vue 提供的 nextTick 方法,但不會產生過度效果他就更新了。所以為了先讓他畫一次畫面,我使用 setTimeout 處理。
動畫進行中避免再有其他觸發事件干擾過度,使用 isAnimation 判斷是否再進行過度效果,並在 transition 結束時 isAnimation 改為 false 關閉過度效果。
Vue.js 模板
<div
  v-for="(timelineEvent, index) in timelineEvents"
  ...
  :class="isAnimation ? 'transition-transform duration-500' : ''"
  ...
  @transitionend="handleAnimationEnd"
>
  //其他 略 
</div>
動畫效果結束資料排序,並關閉 isAnimation 過度效果
const handleAnimationEnd = () => {
    isAnimation.value = false;
    handleSortedTimelineEvents();
};
因為如果我直接針對 timelineEvents 也就是作答的卡片進行排序,就不會產生過度的效果。所以我是這樣處理這個效果。1. 整理出接下來每張卡片要排序的目標定位,為每一張卡片在 style 上綁定目標訂位,此刻資料本身還是錯誤的排序,但畫面上因為 style 的改變會產生移動的效果。2. 移動到正確位置,瞬間重新排序資料。
更新目標位置的方法
const handleUpdateTimelineTargetPosition = () => {
    const sortedTargetLists = [
        ...timelineEvents.value.map((timelineEvent, index) => {
            return {
                ...timelineEvent,
                transform: timelineEventsStyleRaw.value[index].transform,
                zIndex: index + 10,
            };
        }),
    ].sort((a, b) => a.year - b.year);
    sortedTargetLists.forEach((sortedTargetList, index) => {
        timelineEventsStyleRawAnimationTarget.value[index].transform = sortedTargetList.transform;
        timelineEventsStyleRawAnimationTarget.value[index].zIndex = sortedTargetList.zIndex;
    });
};
目前這就是我使用的方法,但為了控制卡片的位置,我使用了 timelineEventsStyleRaw (原本預設每張卡片的排序位置)和 timelineEventsStyleRawAnimationTarget (暫時性用在動畫效果的排序位置),變得程式可讀性很低,我也正在測試不同改進的可能。
另外也試著使用 gsap 或 Anime.js, Velocity.js 搭配 vue transition hooks,但遇到渲染打架的問題,無法很流暢的執行卡片移動的效果。這個目前還在研究中。也期待之後能讓動畫更順暢。